package JavaSE.ThreadLearning.RunnableSellTickets.LockDemo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  卖票案例出现了线程安全问题
 *  卖出了重复或者不存在的票
 *
 *  解决线程安全问题的第三种方案，使用Lock锁
 *  java.util.concurrent.locks.Lock接口
 *  Lock实现提供了比synchronized方法和语句可获得的更广泛的锁定操作
 *  Lock接口中的方法：
 *      void lock() 获取锁
 *      void unlock() 释放锁
 *  java.util.concurrent.locks.ReentrantLock implements Lock接口
 *
 *  使用步骤：
 *      1. 在成员位置创建一个ReentrantLock对象
 *      2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
 *      3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
 *
 */
class SellTicketImpl implements Runnable {
    // 定义一个多个线程共享的漂流
    private int ticket = 100;

    // 1. 在成员位置创建一个ReentrantLock对象
    Lock l = new ReentrantLock();

    // 设置线程任务：卖票
    // 下面这条注解是为了消除while循环的警告信息
    @SuppressWarnings("InfiniteLoopStatement")
    @Override
    public void run() {
        // 使用死循环，让卖票操作重复执行
        while (true) {
            // 2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
            l.lock();
            try {
                // 先判断票是否存在
                if (ticket > 0) {
                    // 票存在，卖票 ticket--
                    System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
                    ticket--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
                // 将unlock方法放在finally块中 即使前面发生异常也可以释放lock锁
                l.unlock();
            }
        }
    }
}
